#include "SEGGER_RTT_Port.h"
#include "SEGGER_RTT.h"


#include "stm32f1xx_hal.h"
#include "main_user.h"

char JS_RTT_UpBuffer[4096];    // J-Scope RTT Buffer
int  JS_RTT_Channel = 1;       // J-Scope RTT Channel

static inline tb_tick_t tb_read_ticks(void)
{
    return (tb_tick_t)DWT->CYCCNT;
    //return (tb_tick_t)tb_tim->CNT;
}


/* -------------------- converter（可替换） --------------------
   把 delta_ticks 转成整数微秒 us_inc 和余数 rem_inc（两者以输出参数返回）
   base_hz     - tick 频率(Hz)，例如 DWT 为 SystemCoreClock，TIM 为 timer_tick_hz
*/
#define s_tb_base_hz  (SystemCoreClock)
//#define s_tb_base_hz  (timer_tick_hz)
static inline void tb_convert_delta_parts(uint32_t delta_ticks, uint64_t *us_inc, uint64_t *rem_inc)
{
    uint64_t tmp = (uint64_t)delta_ticks * 1000000ULL;
    uint64_t base = (uint64_t)s_tb_base_hz;
    *us_inc = tmp / base;
    *rem_inc = tmp % base;
}


/* Internal state */
static volatile uint32_t  s_last_cycles = 0;
static volatile uint64_t  s_us_acc = 0;   /* accumulated microseconds (monotonic) */
static volatile uint64_t  s_rem = 0;      /* accumulated remainder in cycles */


void SEGGER_RTT_TimeStamp_reset(void)
{
    /* 将内部累加归零，并把 last_cycles 设为当前 CYCCNT 以避免下一次产生大 delta */
    __disable_irq();
    s_us_acc = 0;
    s_rem = 0;
    s_last_cycles = tb_read_ticks();
     __enable_irq();
}

/* 返回 32-bit 微秒计数（单调，受 reset 影响） */
uint32_t SEGGER_RTT_TimeStamp_get(void)
{
    uint32_t cur = tb_read_ticks();

    /* 进入临界区更新状态（短期禁中断） */
    __disable_irq();

    uint32_t delta_cycles = cur - s_last_cycles; /* 无符号差自然处理 wrap */
    s_last_cycles = cur;

 
    uint64_t us_inc = 0;
    uint64_t rem_inc = 0;
    /* cycles -> microseconds (分离整数部分与余数以保精度) */
    tb_convert_delta_parts(delta_cycles, &us_inc, &rem_inc);

    s_us_acc += us_inc;
    s_rem += rem_inc;

    if (s_rem >= (uint64_t)s_tb_base_hz) {
        uint64_t carry = s_rem / (uint64_t)s_tb_base_hz;
        s_us_acc += carry;
        s_rem = s_rem % (uint64_t)s_tb_base_hz;
    }


    __enable_irq();

    /* 返回低 32 位（unsigned int），表示微秒时间戳模 2^32 */
    return (unsigned int)(s_us_acc & 0xFFFFFFFFU);
}

void SEGGER_RTT_Port_Init(void) {

    /* Initialize RTT for communication with J-Scope */
    SEGGER_RTT_ConfigUpBuffer(JS_RTT_Channel, "JScope_T4I4I4I4", &JS_RTT_UpBuffer[0], sizeof(JS_RTT_UpBuffer), SEGGER_RTT_MODE_NO_BLOCK_SKIP);
}

extern ADCSamp  ADCSampPare;
#pragma pack(push, 1)
struct {
		unsigned int Timestamp;
		signed int Variable1;
		signed int Variable2;
		signed int Variable3;
} acValBuffer;
#pragma pack(pop)
void SEGGER_RTT_Port_Write(void) {
   
    acValBuffer.Timestamp = SEGGER_RTT_TimeStamp_get();
    acValBuffer.Variable1 = ADCSampPare.PhaseU_CurrFitter;
    acValBuffer.Variable2 = ADCSampPare.PhaseV_CurrFitter;
    
    SEGGER_RTT_Write(JS_RTT_Channel, &acValBuffer, sizeof(acValBuffer));
}



/************************************    J-Scope RTT Plus      ******************************************/
#include <string.h>       // For memset()
//
// J-Scope RTT configurable defines.
//
#define JS_RTT_PLUS_USE_TIMESTAMP (1)         // With or without application defined timestamps.
#define JS_RTT_PLUS_MaxNumVars    (10)        // Maximum number of variables that can be sampled.
#define JS_RTT_PLUS_NumTrys       (0x50000)   // Number of trys to read out the configuration data.

//
// Error messages.
//
#define JS_RTT_PLUS_TIMEOUT   "Timeout while waiting for config data!"
#define JS_RTT_PLUS_VARERROR  "Increase variable buffer!"

//
// J-Scope RTT non configurable defines.
//
#define JS_RTT_PLUS_SUCCESS   "\0"                      // Do not change J-Scope expects this sting otherwise it wont start sampling.
#if JS_RTT_PLUS_USE_TIMESTAMP == 1
  #define JS_RTT_PLUS_UP_NAME "JScope_Data_t"           // RTT UP Buffer name to sample with timestamps.
#else
  #define JS_RTT_PLUS_UP_NAME "JScope_Data"             // RTT UP Buffer name to sample without timestamps.
#endif
#define JS_RTT_PLUS_DOWN_NAME "JScope_Config"           // RTT Down Buffer name


//
// J-Scope RTT related variables.
//
typedef struct JS_RTT_PLUS_SYMBOL {  // Needs to be 64 Bit aligned.
  unsigned long long Addr;             // 64 Bit
  unsigned int       NumBytes;         // 32 Bit
} JS_RTT_PLUS_SYMBOL;

JS_RTT_PLUS_SYMBOL aVarData[JS_RTT_PLUS_MaxNumVars + 1];

int  JS_RTT_PLUS_NumVars;
char JS_RTT_PLUS_UpBuffer[2048];      // J-Scope RTT Up Buffer: Minimum size 1024 Bytes. Set higher for better performance.
char JS_RTT_PLUS_DownBuffer[32];      // J-Scope RTT Down Buffer: Minimum size 16 Bytes.
int  JS_RTT_PLUS_ChannelUp   = 1;     // J-Scope RTT Channel
int  JS_RTT_PLUS_ChannelDown = 2;     // J-Scope RTT Channel

/*********************************************************************
*
*       JS_RTT_PLUS_SendData()
*
* Function description
*   Sends values of the requested variables via RTT to J-Scope.
*
*  Parameter
*    Timestamp: Timestamp in μs (Only used when JS_RTT_PLUS_USE_TIMESTAMP is defined as 1).
*/
void JS_RTT_PLUS_SendData(void) {
  char* pVarData;
  int   VarAddr;
  int   VarSize;
  int   i;
#if JS_RTT_PLUS_USE_TIMESTAMP == 1
  char  acVarData[(JS_RTT_PLUS_NumVars + 1) * 4];       // Data for each variable is max 4 bytes large + 4 bytes timestamp. 
#else
  char  acVarData[JS_RTT_PLUS_NumVars * 4];             // Data for each variable is max 4 bytes large. 
#endif
  // 
  // Read out the requested addresses and variable sizes, then save the values of the variables in the acVarData buffer.
  // Formatting of the data in the paVarData: <4 Byte Addr Var1> <4 Byte size Var1> <4 Byte Addr Var2> <4 Byte size Var2>.
  // Formatting of the data in the acVarData buffer: <4 Byte Timestamp (optional)> <Data var 1> <Data var 2> <Data var 3>. | The size of the individual variable data is based on the size of that variable.
  //
  if (JS_RTT_PLUS_NumVars == 0) {
    return;
  }
  pVarData = &acVarData[0];
#if JS_RTT_PLUS_USE_TIMESTAMP == 1
  *((int*)pVarData) = SEGGER_RTT_TimeStamp_get();                          // Add the timestamp to the buffer.
  pVarData += 4;
#else
  (void)Timestamp;
#endif
  i = 0;
  while (i < JS_RTT_PLUS_NumVars) {
    VarSize = aVarData[i].NumBytes;                      // Get the variables size.
    VarAddr = aVarData[i].Addr;                          // Get the variables address.
    i++;
    switch (VarSize) {
    case 1:
      *pVarData = *((char*)VarAddr);                      // Get the value at the variables address as char and add it to the buffer.
      pVarData += 1;
    break;
    case 2:
      *((short*)pVarData) = *((short*)VarAddr);           // Get the value at the variables address as short and add it to the buffer.
      pVarData += 2;
    break;
    case 4:
      *((int*)pVarData) = *((int*)VarAddr);               // Get the value at the variables address as int and add it to the buffer.
      pVarData += 4;
    break;
    default:
    break;
    }
  }
  SEGGER_RTT_Write(JS_RTT_PLUS_ChannelUp, &acVarData[0], pVarData - &acVarData[0]);  // Send the variable data to J-Scope.
}

/*********************************************************************
*
*       JS_RTT_PLUS_WaitForConfig()
*
* Function description
*   Configures the RTT buffers and waits until the configuration data is sent by J-Scope.
*   When the maximum number of tries is reached, an error message is sent to J-Scope, and the program continues without sampling
*/
void JS_RTT_PLUS_WaitForConfig(void) {
  unsigned char* pVarData;
  int   NumBytes;
  int   NumBytesTotal;
  int   Cnt;
  char  acMsg[256];
  char* sMsg;
  //
  // Configure RTT buffer.
  //
  SEGGER_RTT_ConfigUpBuffer  (JS_RTT_PLUS_ChannelUp,   JS_RTT_PLUS_UP_NAME,   &JS_RTT_PLUS_UpBuffer[0],   sizeof(JS_RTT_PLUS_UpBuffer),   SEGGER_RTT_MODE_BLOCK_IF_FIFO_FULL);
  SEGGER_RTT_ConfigDownBuffer(JS_RTT_PLUS_ChannelDown, JS_RTT_PLUS_DOWN_NAME, &JS_RTT_PLUS_DownBuffer[0], sizeof(JS_RTT_PLUS_DownBuffer), SEGGER_RTT_MODE_BLOCK_IF_FIFO_FULL);
  //
  // Read out the configuration data from the RTT buffer, that is sent by J-Scope.
  //
  memset(&aVarData[0], 0, sizeof(aVarData));
  pVarData      = (unsigned char*)&aVarData[0];
  NumBytes      = 0;
  NumBytesTotal = 0;
  Cnt           = 0;
  while(1) {
    Cnt++;
    if (Cnt > JS_RTT_PLUS_NumTrys) {
      sMsg = JS_RTT_PLUS_TIMEOUT;
      JS_RTT_PLUS_NumVars = 0;
      break;                                                                                           // Error: Timeout.
    }
    NumBytes = SEGGER_RTT_Read(JS_RTT_PLUS_ChannelDown, pVarData, sizeof(aVarData) - NumBytesTotal);   // Read variable data.
    if (NumBytes <= 0) {
      continue;
    }
    pVarData      += NumBytes;
    NumBytesTotal += NumBytes;
    JS_RTT_PLUS_NumVars = NumBytesTotal / sizeof(JS_RTT_PLUS_SYMBOL);
    if (JS_RTT_PLUS_NumVars == 0) {
      continue;
    }
    //
    // Check if we have reached the end of the configuration data.
    //
    if (   (aVarData[JS_RTT_PLUS_NumVars - 1].NumBytes == 0)
        && (aVarData[JS_RTT_PLUS_NumVars - 1].Addr     == 0)) {
      sMsg = JS_RTT_PLUS_SUCCESS;
      JS_RTT_PLUS_NumVars -= 1;                                                                        // Ignore the last dummy entry that just indicates the end.
      break;
    }
    if (NumBytesTotal == sizeof(aVarData)) {
      sMsg = JS_RTT_PLUS_VARERROR;
      JS_RTT_PLUS_NumVars = 0;
      break;                                                                                           // Error: Variable buffer not large enough.
    }
  }
  //
  // Send the reply to J-Scope.
  //
  memset(&acMsg[0], 0, sizeof(acMsg));
  strcpy(&acMsg[0], sMsg);
  SEGGER_RTT_Write(JS_RTT_PLUS_ChannelUp, &acMsg[0], sizeof(acMsg));
}

